﻿using System;
using System.Collections.ObjectModel;
using System.Linq;

namespace Creventive.Wpf.ShellFactory.Utility
{
    /// <summary>
    /// Represents an ObservableCollection which can be sealed.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class SealableObservableCollection<T> : ObservableCollection<T>, ISealable
    {
        private bool isSealed = false;

        /// <summary>
        /// Gets a value indicating whether this instance has been sealed.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance has been sealed; otherwise, <c>false</c>.
        /// </value>
        public bool IsSealed
        {
            get { return this.isSealed; }
        }

        /// <summary>
        /// Clears the items.
        /// </summary>
        protected override void ClearItems()
        {
            this.VerifyUnsealed();
            base.ClearItems();
        }

        /// <summary>
        /// Inserts the item.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <param name="item">The item.</param>
        protected override void InsertItem(int index, T item)
        {
            this.VerifyUnsealed();
            base.InsertItem(index, item);
        }

        /// <summary>
        /// Removes the item.
        /// </summary>
        /// <param name="index">The index.</param>
        protected override void RemoveItem(int index)
        {
            this.VerifyUnsealed();
            base.RemoveItem(index);
        }

        /// <summary>
        /// Sets the item.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <param name="item">The item.</param>
        protected override void SetItem(int index, T item)
        {
            this.VerifyUnsealed();
            base.SetItem(index, item);
        }

        /// <summary>
        /// Verifies that this collection has not been sealed.
        /// </summary>
        protected void VerifyUnsealed()
        {
            if (this.isSealed)
                throw new InvalidOperationException("Cannot modify sealed object");
        }

        /// <summary>
        /// Seals this collection.
        /// </summary>
        public void Seal()
        {
            this.Seal(true);
        }

        /// <summary>
        /// Seals the collection and, optionally, any of its children which is sealable.
        /// </summary>
        /// <param name="sealChildren">if set to <c>true</c> the children should also be sealed.</param>
        public void Seal(bool sealChildren)
        {
            if (!this.isSealed)
            {
                this.OnSealing();
                if (sealChildren)
                    this.OfType<ISealable>().ForEach(s => s.Seal());
                this.isSealed = true;
            }
        }

        /// <summary>
        /// Called when this collection is about to be sealed.
        /// </summary>
        protected virtual void OnSealing()
        {}
    }
}